 /**
 *******************************************************************************
 * @FileName  : user_main.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-05-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include "user_main.h"
#include "utils/soft_uart.h"
#include "utils/mdc.h"
#include "utils/pool_buf.h"
#include "utils/msg.h"
#include "utils/stimer.h"
#include "utils/math.h"
#include "utils/heap.h"
#include "utils/byte_stream.h"
#include "utils/lg_mem.h"

#include "stack/controller/ll_api.h"
#include "stack/controller/bb_api.h"
#include "stack/hci_defs.h"

#include "stack/host/dm_api.h"
#include "stack/host/l2cap_api.h"
#include "stack/host/att_api.h"
#include "stack/host/att_uuid.h"
#include "stack/host/smp_api.h"

#include "profile/svc_handle.h"
#include "component/kv/kv_api.h"
#include "bsp/bsp.h"

#include "sdp.h"

/*!< Heap Size */
#define APP_HEAP_MEM_SIZE           (26 * 1024)
static uint32_t heap[APP_HEAP_MEM_SIZE/sizeof(uint32_t)] = {0};

/*!< LED blink period Unit:ms */
#define APP_LED_EN              1
#define LED_BLINK_PERIOD        500

/*!< Enable MAC Address filter */
#define APP_MAC_FILTER_EN       0
#define APP_FILTER_MAC          0x66, 0x66, 0x66, 0x66, 0x66, 0x66
#define APP_FILTER_LEN          4

/*!< Enable White-list  */
#define APP_WL_EN               1
#define APP_WL_NUM              1

/*!< SDP enable/disable */
#define APP_SDP_EN              1

/*! Connection parameter configuration */
#define APP_MAX_MST_NUM         4
#define APP_MAX_SLV_NUM         4
#define APP_MAX_CONN_NUM        (APP_MAX_MST_NUM + APP_MAX_SLV_NUM)

//Adv configuration
#define APP_ADV_EVT_TYPE        DM_ADV_CONN_UNDIRECT
#define APP_ADV_INTR_MIN        ADV_INTR_30_MS
#define APP_ADV_INTR_MAX        ADV_INTR_35_MS
#define APP_ADV_CHN             LL_ADV_CHAN_ALL
#define APP_ADV_FILT            LL_ADV_FILTER_NONE
#define APP_ADV_LOCAL_ADDR_TYPE LL_ADDR_PUBLIC
#define APP_ADV_PEER_ADDR_TYPE  LL_ADDR_PUBLIC
#define APP_ADV_PEER_ADDR       NULL
//Scan configuration
#define APP_SCAN_TYPE           LL_ACTIVE_SCAN
#define APP_SCAN_INTR           SCAN_INTR_50_MS
#define APP_SCAN_WIN            SCAN_INTR_50_MS
#define APP_SCAN_ADDR_TYPE      LL_ADDR_PUBLIC
#define APP_SCAN_FILTER         (APP_WL_EN ? LL_SCAN_FILTER_WL_BIT : LL_SCAN_FILTER_NONE)
//Connection configuration
#define APP_MST_CONN_INTR       CONN_INTR_50_MS
#define APP_MST_TIMEOUT         400
#define APP_MST_CE_LEN          8  //unit:0.625ms
#define APP_SLV_CONN_INTR       CONN_INTR_30_MS
#define APP_SLV_LATENCY         0
#define APP_SLV_TIMEOUT         400


/*! Link layer config. */
const LlCfg_t llCfg = {
	versNr      : LL_VER_BT_CORE_SPEC, /*!< BLE Core version.           */
	compId      : LL_COMPANY_ID,       /*!< Company ID.                 */
	subVersNr   : LL_SUB_VERSION_NUM,  /*!< Implementation version.     */

	wlNum       : APP_WL_NUM,          /*!< White-list size.            */
	rlNum       : 0,                   /*!< Resolve list size.          */
	connCreateTo: 4,                   /*!< Create connection timeout. 0: disable timeout timer, other: timeout time, unit:s */

	numAdvSet         : 0,
	extAdvDataLen     : 0,
	extScanRspDataLen : 0,
	advMargin         : 0,             /*!< unit:ms */

	maxMst      : APP_MAX_MST_NUM,     /*!< Max number of  master connection. */
	maxSlv      : APP_MAX_SLV_NUM,     /*!< Max number of  slave connection. */
	maxAclLen   : 100,                 /*!< Length of ACL data.         */
	numRxBufs   : 16,                  /*!< Number of Rx buffer.        */
	numTxBufs   : 16,                  /*!< Number of Tx buffer.        */
	localSCA    : 500,                 /*!< Local sleep clock accuracy. */

	mstMargin   : APP_MST_CE_LEN,      /*!< Master connection margin. Unit: 0.625ms*/
	numMoreDat  : 4,                   /*!< the max number of more data. Range: >=1. */

	authPyldTimeoutEn: false,
};


/******************************************************************************/
/* Connection Manager                                                         */
/******************************************************************************/
typedef struct{
	uint32_t        connStartTick;
	uint16_t        connHandle;
	uint8_t         connId;
	uint8_t         role;
	uint8_t         inUse;
	uint8_t         isSmpCmpl;

	MdcTimer_t      phyUpdTimer;
	MdcTimer_t      dleUpdTimer;
	MdcTimer_t      connUpdTimer;
}AppCCB_t;

/*! Application main control block. */
typedef struct{
	AppCCB_t        ccb[APP_MAX_CONN_NUM];

	MdcHandlerId_t  handlerId;
	MdcTimer_t      timer;

	uint8_t         numMstConn;
	uint8_t         numSlvConn;

	uint8_t         startConn;
}AppCb_t;

AppCb_t appCb;

#if APP_SDP_EN
typedef struct{
	SdpCb_t         sdpCb; /* SDP */
	uint16_t        connHandle;
	uint8_t         inUse;
}AppSdpCCB_t;

typedef struct{
	AppSdpCCB_t       sdpCcb[APP_MAX_MST_NUM];
}AppSdpCb_t;

AppSdpCb_t  AppSdpCb;
AppSdpCCB_t *APP_NewSdpCb(uint16_t connHandle)
{
	AppSdpCCB_t *pAppSdpCcb = &AppSdpCb.sdpCcb[0];

	for(int i=0; i<COUNTOF(AppSdpCb.sdpCcb); i++, pAppSdpCcb++)
	{
		if(pAppSdpCcb->inUse == false){
			pAppSdpCcb->inUse = true;
			pAppSdpCcb->connHandle = connHandle;

			SDP_Init(&pAppSdpCcb->sdpCb, connHandle);
			return pAppSdpCcb;
		}
	}
	return NULL;
}

void APP_ReleaseSdpCb(uint16_t connHandle)
{
	AppSdpCCB_t *pAppSdpCcb = &AppSdpCb.sdpCcb[0];

	for(int i=0; i<COUNTOF(AppSdpCb.sdpCcb); i++, pAppSdpCcb++)
	{
		if(pAppSdpCcb->inUse && connHandle == pAppSdpCcb->connHandle){
			pAppSdpCcb->inUse = false;
			pAppSdpCcb->connHandle = 0xFFFF;
			SDP_ParamReset(&pAppSdpCcb->sdpCb);
			return;
		}
	}
}

SdpCb_t *APP_GetSdpCb(uint16_t connHandle)
{
	AppSdpCCB_t *pAppSdpCcb = &AppSdpCb.sdpCcb[0];

	for(int i=0; i<COUNTOF(AppSdpCb.sdpCcb); i++,pAppSdpCcb++)
	{
		if(pAppSdpCcb->inUse && connHandle == pAppSdpCcb->connHandle){
			return &pAppSdpCcb->sdpCb;
		}
	}
	return NULL;
}
#endif

AppCCB_t *APP_AllocCcb(uint16_t connHandle, uint8_t role)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse == false){
			pCcb->inUse = true;
			pCcb->connHandle = connHandle;
			pCcb->role = role;
			pCcb->isSmpCmpl = false;
			pCcb->connId = i+1;

			/* Start PHY Update Timer*/
			MDC_TimerConfigEx(&pCcb->phyUpdTimer, appCb.handlerId, APP_EVT_PHY_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->phyUpdTimer, 500);

			/* Start DLE Update Timer*/
			MDC_TimerConfigEx(&pCcb->dleUpdTimer, appCb.handlerId, APP_EVT_DLE_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->dleUpdTimer, 1000);

			/* Start Connection Update Timer*/
			MDC_TimerConfigEx(&pCcb->connUpdTimer, appCb.handlerId, APP_EVT_CONN_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->connUpdTimer, 4000);

		#if APP_SDP_EN
			if(pCcb->role == LL_ROLE_MASTER){
				APP_NewSdpCb(connHandle);
			}
		#endif
			return pCcb;
		}
	}
	return NULL;
}

void APP_ReleaseCcbByHandle(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			pCcb->inUse = false;
			pCcb->connId = 0;

			MDC_TimerStop(&pCcb->phyUpdTimer);
			MDC_TimerStop(&pCcb->dleUpdTimer);
			MDC_TimerStop(&pCcb->connUpdTimer);

		#if APP_SDP_EN
			APP_ReleaseSdpCb(connHandle);
		#endif
			return;
		}
	}
}

AppCCB_t *APP_GetConnCb(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			return pCcb;
		}
	}
	return NULL;
}

void APP_DisconnectAll(void)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse){
			HCI_Disconnect(pCcb->connHandle, HCI_ERR_REMOTE_USER_TERM_CONN);
		}
	}
}

void APP_MsgHandler(MdcEventMask_t event, MdcMsgHdr_t *pMsg)
{
	if(pMsg)
	{
		switch(pMsg->event)
		{
		case APP_EVT_LED_BLINK:
			BSP_LedToggle(LED_GREEN_PIN);
			MDC_TimerStartMs(&appCb.timer, LED_BLINK_PERIOD);
			break;


		case APP_EVT_PHY_UPD:
		{
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT, LL_PHYS_LE_1M_BIT, LL_PHY_OPTIONS_NONE);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_2M_BIT, LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S8_PREFERRED);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S2_PREFERRED);
			//LL_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);

			//APP_TRACK_WRN("Start PHY Upd...\r\n");
			break;
		}
		case APP_EVT_DLE_UPD:
		{
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//HCI_LE_SetDataLen(connHandle, llCfg.maxAclLen, LL_MAX_TX_RX_TIME_1M(llCfg.maxAclLen));
			//APP_TRACK_WRN("Start DLE Upd...\r\n");
			break;
		}
		case APP_EVT_CONN_UPD:
		{
			AppCCB_t *pCCB = APP_GetConnCb(pMsg->param);
			if(pCCB)
			{
				if(pCCB->role == DM_ROLE_SLAVE){
					L2C_SlvConnUpdateReq(pCCB->connHandle, APP_SLV_CONN_INTR, APP_SLV_CONN_INTR,
														   APP_SLV_LATENCY,   APP_SLV_TIMEOUT);
					APP_TRACK_WRN("Slave L2CAP Send conn update req\r\n");
				}
				else{
					//APP_MstConnUpd(pCCB->connHandle);
				}
			}
			break;
		}

		default:
			break;
		}
	}

	if(event)
	{
	}
}

/******************************************************************************/
/* Key                                                                        */
/******************************************************************************/
//#define MKEY_VOL_UP        0x00E9
//#define MKEY_VOL_DN        0x00EA

#define KEY_TYPE_NONE      0x00
#define KEY_TYPE_KEYBOARD  0x01
#define KEY_TYPE_CONSUME   0x02

static uint8_t  keyType     = KEY_TYPE_NONE;
static uint8_t  keyPressed  = false;
static uint32_t keyScanTick = 0;

uint8_t A_dataSendStart = 0;
uint32_t A_dataSendTimer = 0;

void App_KeyInit(void)
{
	KB_Init();
}

void App_SendKeyReport(uint16_t attHandle, uint8_t *pData, uint32_t len)
{
	AppCCB_t *pCcb = &appCb.ccb[0];
	for(int i=0; i<APP_MAX_CONN_NUM; i++, pCcb++)
	{
		if(pCcb->inUse && pCcb->role == DM_ROLE_SLAVE){
			ATTS_ValueNotify(pCcb->connHandle, attHandle, pData, len);
		}
	}
}

void App_KeyChangeHandler(void)
{
	uint8_t  key0 = kbCb.keyCode[0];
	uint16_t consumerKey = 0;
	uint8_t  keyboard[8]={0};

	keyPressed = true;

	switch(kbCb.cnt)
	{
	case 0://key release
		keyPressed = false;
		if(keyType & KEY_TYPE_CONSUME){
			App_SendKeyReport(HID_INPUT_REPORT_2_HDL, (uint8_t*)&consumerKey, 2);
		}
		if(keyType & KEY_TYPE_KEYBOARD){
			App_SendKeyReport(HID_INPUT_REPORT_1_HDL, (uint8_t*)&keyboard, 8);
		}
		keyType = KEY_TYPE_NONE;

		appCb.startConn = false;
		break;

	case 1:// one key pressed
		if(key0 == CR_VOL_UP){
			consumerKey = MKEY_VOL_UP;
			A_dataSendStart = true;
			BSP_LedOn(LED_BLUE_PIN);
			APP_TRACK_INFO("data send START...\r\n");
		}
		else if(key0 == CR_VOL_DN){
			consumerKey = MKEY_VOL_DN;
			A_dataSendStart = false;
			BSP_LedOff(LED_BLUE_PIN);
			APP_TRACK_INFO("data send STOP...\r\n");
		}
		else if(key0 == BTN_PAIR){
			keyboard[2] = 0x1e;

			appCb.startConn = true;
			//APP_TRACK_INFO("Start connect...\r\n");

		}else if(key0 == BTN_UNPAIR){
			keyboard[2] = 0x1f;

			/* Disconnect all connections. */
			APP_DisconnectAll();
			APP_TRACK_INFO("Disconnect all...\r\n");
		}

		if(key0 == CR_VOL_UP || key0 == CR_VOL_DN){
			keyType |= KEY_TYPE_CONSUME;
		}else if(key0 == BTN_PAIR || key0 == BTN_UNPAIR){
			keyType |= KEY_TYPE_KEYBOARD;
		}else{
			keyType = KEY_TYPE_NONE;
		}

		if(keyType & KEY_TYPE_CONSUME){
			App_SendKeyReport(HID_INPUT_REPORT_2_HDL, (uint8_t*)&consumerKey, 2);
		}else if(keyType & KEY_TYPE_KEYBOARD){
			App_SendKeyReport(HID_INPUT_REPORT_1_HDL, (uint8_t*)&keyboard, 8);
		}
		break;

	case 2://two key pressed
		break;

	default:
		break;
	}
}

void App_KeyTaskStart(void)
{
	if(!SysTimer_IsTimeExpired(keyScanTick, 20 * 1000)){  //keyScan interval: 20mS
		return;
	}
	keyScanTick = SysTimer_GetCurTick();

	bool_t keyChanged = KB_Scan();

	if(keyChanged){
		App_KeyChangeHandler();
	}
}

/******************************************************************************/
/* Event Call-back                                                            */
/******************************************************************************/
#if APP_WL_EN
bdAddr_t addrTbl[] = {
	{0x66, 0x66, 0x66, 0x66, 0x00, 0x00},
};
#endif

uint32_t A_createConnStart = 0;
uint32_t A_connCnt = 0;
uint8_t remoteAddr[6] = {0x66, 0x66, 0x66, 0x66, 0x66, 0x66};

LlInitParam_t initParam = {
	APP_SCAN_INTR,       /*!< scan interval */
	APP_SCAN_WIN,        /*!< scan window size */
	LL_INIT_FILTER_PEER, /*!< filter policy */
	LL_ADDR_PUBLIC,      /*!< local address type */
	LL_ADDR_PUBLIC,      /*!< peer address type */
	&remoteAddr[0],      /*!< local address */
};

LlConnSpec_t connSpec = {
	APP_MST_CONN_INTR,  /*!< Min connection interval */
	APP_MST_CONN_INTR,  /*!< Max connection interval */
	0,                  /*!< latency */
	APP_MST_TIMEOUT,    /*!< timeout */
	APP_MST_CE_LEN,     /*!< min connection event(CE) length, unit:0.625ms */
	APP_MST_CE_LEN,     /*!< max connection event(CE) length, unit:0.625ms */
};

void APP_ConnUpd(uint16_t connHandle)
{
	LlConnSpec_t connSpec;
	connSpec.connIntervalMin = MSEC_TO_UNITS(1000, UNIT_1_25_MS);
	connSpec.connIntervalMax = MSEC_TO_UNITS(1000, UNIT_1_25_MS);
	connSpec.connLatency = 0;
	connSpec.supTimeout = MSEC_TO_UNITS(4000, UNIT_10_MS);
	connSpec.minCeLen = 0;
	connSpec.maxCeLen = 0;
	HCI_LE_ConnUpd(connHandle, &connSpec);
}

void DM_EvtHandler(DmEvt_t *pEvt)
{
	switch(pEvt->hdr.event)
	{
	case DM_ADV_REPORT_IND:
	{
		HciLeAdvReportEvt_t *p = (HciLeAdvReportEvt_t *)pEvt;

		//APP_TRACK_INFO("Scan BD_ADDR: ");
		//APP_TRACK_DATA((uint8_t *)&p->addr, 6);
		//APP_TRACK_INFO("adv rssi: %d\r\n", p->rssi);

		if(p->eventType >= HCI_ADV_DISC_UNDIRECT || p->rssi < -56){
			return;
		}
		//APP_TRACK_INFO("adv rssi: %d\r\n", p->rssi);

		if(appCb.numMstConn >= APP_MAX_MST_NUM){
			return;
		}

	  #if APP_MAC_FILTER_EN
		// user address filter
		uint8_t filterMac[] = {APP_FILTER_MAC};
		if(memcmp((uint8_t*)&p->addr, filterMac, APP_FILTER_LEN)){
			return;
		}
	  #endif

		if(appCb.startConn)
		{
			APP_TRACK_INFO("Start Connection...\r\n");
			appCb.startConn = false;
			memcpy(remoteAddr, p->addr, 6);
			LL_LE_CreateConn(&initParam, &connSpec);
		}
		break;
	}
	case DM_CONN_OPEN_IND:
	{
		if(pEvt->leConnCmpl.status){//Create connection cancel
			if(appCb.numMstConn < APP_MAX_MST_NUM){
				DM_LE_SetScanEnable(ENABLE, DISABLE);
			}
			APP_TRACK_WRN("Create connection timeout, Restart scan...\r\n");
			return;
		}

		A_connCnt++;

		APP_TRACK_INFO("****** Connection Cmpl ******\r\n\t"
				       "-connHandle:%d\r\n\t"
					   "-role:%s\r\n\t"
					   "-addr_type:%d\r\n\t"
				       "-peer_addr: ",
					   pEvt->leConnCmpl.handle,
					   pEvt->leConnCmpl.role == DM_ROLE_MASTER ? "Master": "Slave",
					   pEvt->leConnCmpl.peerAddrType);
		TRACK_DATA((uint8_t*)&pEvt->leConnCmpl.peerAddr, 6);

		BSP_LedOn(LED_RED_PIN);
		A_createConnStart = SysTimer_GetCurTick()|1;
		AppCCB_t *pCCB = APP_AllocCcb(pEvt->leConnCmpl.handle, pEvt->leConnCmpl.role);
		if(pCCB){
			pCCB->connStartTick = GetCurSysTick()|1;
		}

		if(pEvt->leConnCmpl.role == DM_ROLE_MASTER){
			appCb.numMstConn++;
			if(appCb.numMstConn < APP_MAX_MST_NUM){
				DM_LE_SetScanEnable(ENABLE, DISABLE);
			}
			HCI_ReadRemoteVerInfo(pEvt->leConnCmpl.handle);
		}
		else if(pEvt->leConnCmpl.role == DM_ROLE_SLAVE){
			appCb.numSlvConn++;
			if(appCb.numSlvConn < APP_MAX_SLV_NUM){
				DM_LE_SetAdvEnable(ENABLE);
			}
		}
		break;
	}
	case DM_CONN_CLOSE_IND:
	{
		A_connCnt--;
		APP_TRACK_INFO("******Disconnection Cmpl******\r\n"
				"\t-connHandle: 0x%02x\r\n"
				"\t-reason: 0x%02x\r\n",
				pEvt->disconnectCmpl.handle, pEvt->disconnectCmpl.reason);

		BSP_LedOff(LED_RED_PIN);

		uint8_t role = APP_GetConnCb(pEvt->disconnectCmpl.handle)->role;

		if(appCb.numMstConn && role == DM_ROLE_MASTER){
			appCb.numMstConn--;
			DM_LE_SetScanEnable(ENABLE, DISABLE);
		}
		else if(appCb.numSlvConn && role == DM_ROLE_SLAVE){
			appCb.numSlvConn--;
			DM_LE_SetAdvEnable(ENABLE);
			APP_TRACK_INFO("app adv enable...\r\n");
		}
		APP_ReleaseCcbByHandle(pEvt->disconnectCmpl.handle);
		break;
	}
	case DM_CONN_READ_REMOTE_VER_IND:
		APP_TRACK_INFO("Remote Version: %d\r\n\t-manufacturer ID:0x%04x\r\n", pEvt->readRemoteVerInfoCmpl.version,pEvt->readRemoteVerInfoCmpl.mfrName);
		HCI_LE_ReadRemoteFeat(pEvt->readRemoteVerInfoCmpl.handle);
		break;

	case DM_CONN_LE_READ_REMOTE_FEAT_IND:
		APP_TRACK_INFO("LE Remote Feature: 0x%02x\r\n",pEvt->leReadRemoteFeatCmpl.features[0]);
		break;

	case DM_CONN_DATA_LEN_CHANGE_IND:
		APP_TRACK_INFO("LE data length changed\r\n\t-max tx: %d B\r\n\t-max rx: %d B\r\n",
				       pEvt->leDataLenChange.maxTxOctets, pEvt->leDataLenChange.maxRxOctets);
		break;

	case DM_CONN_PHY_UPD_IND:
		APP_TRACK_INFO("Phy upd cmpl\r\n\t-Phy:%d\r\n", pEvt->lePhyUpdCmpl.txPhy);
		break;

	case DM_CONN_CH_SEL_IND:
		APP_TRACK_INFO("Chn Select cmpl\r\n\t-Chn:%d\r\n", pEvt->leChnSelInd.chSelAlg);
		break;

	case DM_L2C_CONN_PARAM_UPD_IND:
		break;

	case DM_CONN_UPD_IND:
		APP_TRACK_INFO("Conn Upd Cmpl:\r\n\t-intvl:%d us\r\n\t-latency:%d\r\n\t-to:%d ms\r\n",
				       pEvt->leConnUpdateCmpl.connInterval *UNIT_1_25_MS,
					   pEvt->leConnUpdateCmpl.connLatency,
				       pEvt->leConnUpdateCmpl.supTimeout * 10);
		break;

	case DM_SMP_PIN_CODE_REQ:
	{
		uint32_t pinCode = 123456;
		SMP_SetPinCode(pEvt->smpPinCodeReq.connHandle, pinCode);
		APP_TRACK_INFO("%6d\r\n", pinCode);
		break;
	}

	case DM_SMP_PAIR_END_IND:
	{
		if(pEvt->smpPairEndInd.status){
			APP_TRACK_INFO("Pair failed...connHandle: 0x%02x\r\n", pEvt->smpPairEndInd.connHandle);
		}
		else{
			APP_TRACK_INFO("Pair successfully...connHandle: 0x%02x\r\n", pEvt->smpPairEndInd.connHandle);
			AppCCB_t *pCcb = APP_GetConnCb(pEvt->smpPairEndInd.connHandle);
			if(pCcb){
				pCcb->isSmpCmpl = true;
			}
		}
		#if APP_SDP_EN
			SmpPairEndInd_t *pCmpl = (SmpPairEndInd_t *)pEvt;
			AppCCB_t *pCcb = APP_GetConnCb(pCmpl->connHandle);
			if(pCcb && pCcb->role == LL_ROLE_MASTER){
				SdpCb_t *pSdpCb = APP_GetSdpCb(pCcb->connHandle);
				SDP_Start(pSdpCb);
			}
		#endif
		break;
	}

	default:
		break;
	}
}

void ATT_EvtHandler(AttEvt_t *pEvt)
{
	AppCCB_t *pCcb = APP_GetConnCb(pEvt->handle);
	if(pCcb == NULL){
		return;
	}
#if APP_SDP_EN
	SdpCb_t *pSdpCb = APP_GetSdpCb(pEvt->handle);
#endif
	switch(pEvt->hdr.event)
	{
	case ATTC_ERR_RSP:
	{
	#if APP_SDP_EN
		if(pSdpCb){
			SDP_GattErrRspHandler(pSdpCb, pEvt->pValue, pEvt->valueLen);
		}
	#endif
		break;
	}
	case ATTC_READ_BY_GROUP_TYPE_RSP:
	#if APP_SDP_EN
		if(pSdpCb){
			SDP_GattDataHandler(pSdpCb, pEvt->pValue, pEvt->valueLen);
		}
	#endif
		break;

	case ATTC_READ_BY_TYPE_RSP:
	#if APP_SDP_EN
		if(pSdpCb){
			SDP_GattDataHandler(pSdpCb, pEvt->pValue, pEvt->valueLen);
		}
	#endif
		break;

	case ATTC_HANDLE_VALUE_NTF:
	{
	#if APP_SDP_EN
		if(pSdpCb == NULL)
			return;

		uint16_t attHandle = 0;
		uint16_t keyCode = 0;
		uint16_t uuid = ATT_UUID_HID_REPORT;
		uint8_t *pPkt = pEvt->pValue;

		//APP_TRACK_INFO("RX Notify:");
		//APP_TRACK_DAT(pEvt->pValue, pEvt->valueLen);

		pPkt++; //skip opcode
		BSTREAM_TO_UINT16(attHandle, pPkt);
		BSTREAM_TO_UINT16(keyCode, pPkt);

		Att_t *pAtt = SDP_FindChar(pSdpCb, attHandle);
		if(pAtt && pAtt->valueUuid.len == ATT_16_UUID_LEN && pAtt->valueUuid.uuid_16 == uuid)
		{
			if(keyCode == 0x00E9 || keyCode == 0x0001){
				BSP_LedToggle(LED_BLUE_PIN);
			}
			else if(keyCode == 0x00EA || keyCode == 0x0002){
				BSP_LedToggle(LED_WHITE_PIN);
			}
		}
	#endif
		break;
	}

	case ATTC_HANDLE_VALUE_IND:
		//APP_TRACK_WRN("Send value confirm\r\n");
		ATTC_SendValueConfirm(pCcb->connHandle);
		break;

	case ATT_MTU_UPDATE_IND:
		APP_TRACK_INFO("MTU Update Cmpl\r\n\t-MTU size:%d B\r\n", pEvt->mtu);
		break;

	default:
		break;
	}
}

#if APP_SDP_EN
void SDP_EvtHandler(SdpEvt_t *pEvt)
{
	switch(pEvt->evtId)
	{
	case SDP_EVT_TIMEOUT:
		break;

	case SDP_EVT_SDP_END:
		break;

	default:
		break;
	}
}
#endif

void APP_Init(void)
{
	appCb.handlerId = MDC_AddMsgHandler(APP_MsgHandler);
	appCb.timer.handlerId = appCb.handlerId;
	appCb.timer.msg.event = APP_EVT_LED_BLINK;

#if APP_LED_EN
	MDC_TimerStartMs(&appCb.timer, LED_BLINK_PERIOD);
#endif

	App_KeyInit();
}

void APP_Start(void)
{
#if APP_SDP_EN
	for(int i=0; i<APP_MAX_MST_NUM; i++)
	{
		SDP_Handler(&AppSdpCb.sdpCcb[i].sdpCb);
	}
#endif

	App_KeyTaskStart();
}

/**
 * @brief: setup
 */
void Setup(void)
{
	/* memory initialization. */
	lg_mem_init();
	HeapInit((uint8_t *)&heap[0], APP_HEAP_MEM_SIZE);

	/* KV initialization. */
	kv_init(CFG_KV_FLASH_BASE_ADDR, CFG_KV_BLOCK_SIZE, CFG_KV_BLOCK_NUM);

	/* BSP initialization. */
	BSP_InitAllLed();

	/* Buffer pool alloc. */
	pool_desc_t pools[] ={
		{16,     8},
		{32,     8},
		{80,     8},
	    {280,    4},
	};
	PoolBufInit(pools, COUNTOF(pools));

	/* MDC system initialization. */
	MDC_Init();
	MDC_TimerInit();/*!< MDC Timer initialization. */

	/* Set MAC address. */
	uint8_t bdAddr[6] = {0x66, 0x66, 0x66, 0x66, 0x00, 0x00};
	GetConfigParam(CFG_PARAM_MAC_ADDR, bdAddr, 6);
	LL_SetBdAddr(bdAddr, BD_ADDR_LEN);

	APP_TRACK_INFO("BD_ADDR:");
	APP_TRACK_DAT(bdAddr, BD_ADDR_LEN);

	/* LL initialization. */
	LL_Init(&llCfg);
	LL_MstInit();
	LL_SlvInit();
	BB_SetTxPower(BB_POWER_P3p01_dBm);

	hostCfg.numMaxConn = APP_MAX_CONN_NUM;

	/* HCI initialization. */
	HCI_Init();
	HCI_SetMaxAclLen(247 + 4);/*!< Set L2CAP payload size= 104B ==> ATT MTU Size = 100. */

	/* DM initialization. */
	DM_Init();
	DM_AdvInit();
	DM_ScanInit();
	DM_SecInit();
	DM_EvtRegister(DM_EvtHandler);
	DM_SecSetInitiateSecEnable(true);

	/* L2CAP initialization. */
	L2C_Init();
	L2C_SlvInit();
	L2C_MstInit();

	/* ATT initialization. */
	ATT_Init();
	ATTS_Init();
	ATTC_Init();
	ATT_EvtRegister(ATT_EvtHandler);
	ATTS_CccRegister(&ServerCccSets[0], CCC_IDX_MAX, NULL);

	/* Register GATT table */
	ATTS_AddAttGroup(&gapGrp);
	ATTS_AddAttGroup(&gattGrp);
	ATTS_AddAttGroup(&battGrp);
	ATTS_AddAttGroup(&hidGrp);
	ATTS_AddAttGroup(&gwGrp);
	ATTS_AddAttGroup(&htsGrp);

	/* SMP initialization. */
#if 0
	SMP_NonInit();
#else
	SMP_Init();
	SMP_RespInit();
	SMP_InitiatorInit();
#endif

	/* ADV initialization. */
	DM_LE_SetAdvParam(APP_ADV_INTR_MIN,        /*!< min adv interval.   */
			          APP_ADV_INTR_MAX,        /*!< max adv interval.   */
			          APP_ADV_EVT_TYPE,        /*!< adv event type.     */
			          APP_ADV_LOCAL_ADDR_TYPE, /*!< local address type. */
			          APP_ADV_PEER_ADDR_TYPE,  /*!< peer address type.  */
			          NULL,                    /*!< peer address.       */
			          APP_ADV_CHN,             /*!< adv channel map.    */
			          APP_ADV_FILT);           /*!< adv filter policy.  */

	uint8_t advData[] = {
		0x02, 0x01, 0x06,         /*!< Flag type.  */
		0x0B, 0x08, 'M','u','l','t','i','-','C','o','n','n',/*!< short name. */
		0x03, 0x19, 0x80, 0x01,   /*!< 384, Generic Remote Control, Generic category */
		0x03, 0x02, 0x12, 0x18,   /*!< service UUID */
	};
	uint8_t scanRsp[] = {
		0x0B, 0x09, 'M','u','l','t','i','-','C','o','n','n',  /*!< complete name. */
	};
	DM_LE_SetAdvData(advData, sizeof(advData));
	DM_LE_SetScanRspData(scanRsp, sizeof(scanRsp));

#if APP_MAX_SLV_NUM
	DM_LE_SetAdvEnable(ENABLE);
#endif

    /* Scan initialization. */
	DM_LE_SetScanParam(APP_SCAN_TYPE,          /*!< scan type.          */
			           APP_SCAN_INTR,          /*!< scan interval.      */
			           APP_SCAN_WIN,           /*!< scan window.        */
			           APP_SCAN_ADDR_TYPE,     /*!< local address type. */
			           APP_SCAN_FILTER);       /*!< scan filter policy. */
#if APP_MAX_MST_NUM
	DM_LE_SetScanEnable(ENABLE, DISABLE);
#endif

#if APP_WL_EN
	/* Setting Whitelist */
	for(uint16_t i=0; i<APP_WL_NUM; i++){
		DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addrTbl[i]);
	}
#endif

#if APP_SDP_EN
	SDP_RegisterEvtCback(SDP_EvtHandler);
#endif

	/* Application initialization. */
	APP_Init();
}

bool_t ATT_TxBufAvailable(uint16_t connHandle)
{
	uint32_t minNum = ((4 + ATT_GetMtu(connHandle)) / pLlConfig->maxAclLen + 1);
	return LL_GetFreeTxBufNum(connHandle) >= minNum;
}

#if 0
uint8_t data1[] = {
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,//80
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,//80
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,//80
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
};

uint8_t dataCnt[APP_MAX_CONN_NUM] = {0};
#endif

uint32_t A_loop = 0;
void Loop(void)
{
	A_loop++;

	MDC_Start();

	APP_Start();

#if 0
	if(A_dataSendStart && SysTimer_IsTimeExpired(A_dataSendTimer, 3*1000))
	{
		A_dataSendTimer = SysTimer_GetCurTick()|1;

		AppCCB_t *pCcb = appCb.ccb;
		for(int i=0; i<APP_MAX_CONN_NUM; i++, pCcb++)
		{
			if(pCcb->inUse)
			{
				if(pCcb->role == DM_ROLE_MASTER && ATT_TxBufAvailable(pCcb->connHandle)){
					uint8_t *p = data1;
					uint32_t res = ATT_SUCCESS;
					p[0] = dataCnt[pCcb->connHandle];
					if((res = ATTC_SendWriteCmd(pCcb->connHandle, GW_RX_VALUE_HDL, p, sizeof(data1))) == ATT_SUCCESS){
						dataCnt[pCcb->connHandle]++;
					}
					else{
						//APP_TRACK_ERR("send data failed-res:%02X, connHandle:%02x\r\n", res, pCcb->connHandle);
					}
				}
				else if(pCcb->role == DM_ROLE_SLAVE && ATT_TxBufAvailable(pCcb->connHandle)){
					uint8_t *p = data1;
					p[0] = dataCnt[pCcb->connHandle];
					if(GW_SendData(pCcb->connHandle, p, sizeof(data1)) == BLE_OK){
						dataCnt[pCcb->connHandle]++;
					}
				}
				else{
					//APP_TRACK_WRN("ll tx buf num:%d\r\n", LL_GetFreeTxBufNum(pCcb->connHandle));
				}
			}
		}
	}
#endif
}
